哈囉,大家好!在前面的文章中,我們已經實作了銀行帳戶管理和分類管理的功能,建立了相應的列表頁面以及新增/編輯頁面。
今天,我們將繼續完善我們的個人財務管理系統,專注於交易紀錄管理的開發。
交易紀錄是財務管理系統的核心,透過記錄每一筆收入和支出,我們可以清楚地了解自己的財務狀況。
接下來,我們將建立交易紀錄列表頁面以及新增/編輯交易紀錄頁面,讓使用者可以方便地管理他們的交易紀錄。
首先,我們要建立交易紀錄列表頁面,讓使用者可以查看、編輯和刪除他們的交易紀錄。
在 pages/ 目錄下建立 transactions.vue 檔案。
touch pages/transactions.vue
在 transactions.vue 中,我們建立基本的頁面結構,顯示交易紀錄列表。
<template>
<div>
<h2 class="text-2xl font-bold mb-4">交易紀錄</h2>
<div v-if="error" class="text-red-500">
{{ error }}
</div>
<div v-else>
<button @click="goToAddTransaction" class="bg-blue-600 text-white px-4 py-2 mb-4">
新增交易
</button>
<table class="w-full text-left">
<thead>
<tr>
<th class="border px-4 py-2">日期</th>
<th class="border px-4 py-2">類型</th>
<th class="border px-4 py-2">分類</th>
<th class="border px-4 py-2">金額</th>
<th class="border px-4 py-2">帳戶</th>
<th class="border px-4 py-2">描述</th>
<th class="border px-4 py-2">操作</th>
</tr>
</thead>
<tbody>
<tr v-for="transaction in transactions" :key="transaction.id">
<td class="border px-4 py-2">{{ transaction.date }}</td>
<td class="border px-4 py-2">{{ transaction.type === 'income' ? '收入' : '支出' }}</td>
<td class="border px-4 py-2">{{ transaction.category.name }}</td>
<td class="border px-4 py-2">{{ transaction.amount }}</td>
<td class="border px-4 py-2">{{ transaction.bank_account.account_name }}</td>
<td class="border px-4 py-2">{{ transaction.description }}</td>
<td class="border px-4 py-2">
<button @click="goToEditTransaction(transaction.id)" class="text-blue-600 mr-2">
編輯
</button>
<button @click="deleteTransaction(transaction.id)" class="text-red-600">
刪除
</button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</template>
<script>
export default {
middleware: 'auth',
data() {
return {
transactions: [],
error: null,
}
},
async mounted() {
try {
const response = await this.$axios.get('/api/transactions')
if (response.data.status === 'success') {
this.transactions = response.data.data
} else {
this.error = response.data.message || '無法獲取交易紀錄資料。'
}
} catch (error) {
this.error = '伺服器發生錯誤,請稍後再試。'
console.error('Error fetching transactions:', error)
}
},
methods: {
goToAddTransaction() {
this.$router.push('/transactions/add')
},
goToEditTransaction(id) {
this.$router.push(`/transactions/edit/${id}`)
},
async deleteTransaction(id) {
if (confirm('確定要刪除這筆交易嗎?')) {
try {
await this.$axios.delete(`/api/transactions/${id}`)
this.transactions = this.transactions.filter(transaction => transaction.id !== id)
} catch (error) {
alert('刪除失敗,請稍後再試。')
console.error('Error deleting transaction:', error)
}
}
},
},
}
</script>
<style scoped>
/* 頁面專屬的樣式 */
</style>
接下來,我們建立新增和編輯交易紀錄的頁面,使用共用的表單元件來實現。
在 pages/transactions/ 目錄下建立以下檔案:
mkdir -p pages/transactions
touch pages/transactions/add.vue
touch pages/transactions/_id.vue
touch pages/transactions/_form.vue
在 _form.vue 中,我們建立交易紀錄的表單元件,供新增和編輯頁面使用。
<template>
<div>
<h2 class="text-2xl font-bold mb-4">{{ isEdit ? '編輯交易' : '新增交易' }}</h2>
<form @submit.prevent="submitForm">
<div class="mb-4">
<label class="block">日期</label>
<input v-model="form.date" type="date" class="border p-2 w-full" required />
<p v-if="errors.date" class="text-red-500">{{ errors.date }}</p>
</div>
<div class="mb-4">
<label class="block">類型</label>
<select v-model="form.type" @change="fetchCategories" class="border p-2 w-full" required>
<option value="">請選擇</option>
<option value="income">收入</option>
<option value="expense">支出</option>
</select>
<p v-if="errors.type" class="text-red-500">{{ errors.type }}</p>
</div>
<div class="mb-4">
<label class="block">分類</label>
<select v-model="form.category_id" class="border p-2 w-full" required>
<option value="">請選擇</option>
<option v-for="category in categories" :key="category.id" :value="category.id">
{{ category.name }}
</option>
</select>
<p v-if="errors.category_id" class="text-red-500">{{ errors.category_id }}</p>
</div>
<div class="mb-4">
<label class="block">金額</label>
<input v-model="form.amount" type="number" step="0.01" class="border p-2 w-full" required />
<p v-if="errors.amount" class="text-red-500">{{ errors.amount }}</p>
</div>
<div class="mb-4">
<label class="block">帳戶</label>
<select v-model="form.bank_account_id" class="border p-2 w-full" required>
<option value="">請選擇</option>
<option v-for="account in bankAccounts" :key="account.id" :value="account.id">
{{ account.account_name }}
</option>
</select>
<p v-if="errors.bank_account_id" class="text-red-500">{{ errors.bank_account_id }}</p>
</div>
<div class="mb-4">
<label class="block">描述</label>
<textarea v-model="form.description" class="border p-2 w-full"></textarea>
<p v-if="errors.description" class="text-red-500">{{ errors.description }}</p>
</div>
<div v-if="error" class="text-red-500 mb-4">
{{ error }}
</div>
<button type="submit" class="bg-blue-600 text-white px-4 py-2">
{{ isEdit ? '更新' : '新增' }}
</button>
</form>
</div>
</template>
<script>
export default {
props: {
isEdit: {
type: Boolean,
default: false,
},
transactionId: {
type: Number,
default: null,
},
},
data() {
return {
form: {
date: '',
type: '',
category_id: '',
amount: 0,
bank_account_id: '',
description: '',
},
errors: {},
error: null,
categories: [],
bankAccounts: [],
}
},
async mounted() {
await this.fetchBankAccounts()
if (this.isEdit && this.transactionId) {
try {
const response = await this.$axios.get(`/api/transactions/${this.transactionId}`)
if (response.data.status === 'success') {
this.form = response.data.data
await this.fetchCategories()
} else {
this.error = response.data.message || '無法獲取交易資料。'
}
} catch (error) {
this.error = '伺服器發生錯誤,請稍後再試。'
console.error('Error fetching transaction:', error)
}
}
},
methods: {
async fetchCategories() {
if (!this.form.type) return
try {
const response = await this.$axios.get(`/api/categories?type=${this.form.type}`)
if (response.data.status === 'success') {
this.categories = response.data.data
} else {
this.error = response.data.message || '無法獲取分類資料。'
}
} catch (error) {
this.error = '伺服器發生錯誤,請稍後再試。'
console.error('Error fetching categories:', error)
}
},
async fetchBankAccounts() {
try {
const response = await this.$axios.get('/api/bank-accounts')
if (response.data.status === 'success') {
this.bankAccounts = response.data.data
} else {
this.error = response.data.message || '無法獲取銀行帳戶資料。'
}
} catch (error) {
this.error = '伺服器發生錯誤,請稍後再試。'
console.error('Error fetching bank accounts:', error)
}
},
async submitForm() {
this.errors = {}
this.error = null
try {
if (this.isEdit) {
await this.$axios.put(`/api/transactions/${this.transactionId}`, this.form)
} else {
await this.$axios.post('/api/transactions', this.form)
}
this.$router.push('/transactions')
} catch (error) {
if (error.response && error.response.status === 422) {
this.errors = error.response.data.errors
} else {
this.error = '提交失敗,請稍後再試。'
}
console.error('Error submitting transaction form:', error)
}
},
},
}
</script>
<style scoped>
/* 元件專屬的樣式 */
</style>
<template>
<FormComponent />
</template>
<script>
import FormComponent from './_form.vue'
export default {
middleware: 'auth',
components: {
FormComponent,
},
}
</script>
<style scoped>
/* 頁面專屬的樣式 */
</style>
<template>
<FormComponent :isEdit="true" :transactionId="transactionId" />
</template>
<script>
import FormComponent from './_form.vue'
export default {
middleware: 'auth',
components: {
FormComponent,
},
computed: {
transactionId() {
return parseInt(this.$route.params.id)
},
},
}
</script>
<style scoped>
/* 頁面專屬的樣式 */
</style>
為了方便使用者訪問交易紀錄管理頁面,我們需要在導航列中添加對應的連結。
如果之前已經有交易紀錄的連結,可以忽略此步驟,否則請添加以下內容:
<!-- 在導航列中添加交易紀錄的連結 -->
<li><NuxtLink to="/transactions">交易紀錄</NuxtLink></li>
現在,我們已經完成了交易紀錄列表頁面和新增/編輯頁面的開發。讓我們進行測試,確保功能正常運作。
今天,我們成功地實作了交易紀錄列表頁面以及新增/編輯頁面。透過這次的開發,我們學習了:
希望這篇文章能夠對你有所幫助,讓我們一起繼續學習和進步,打造出更加完善的應用程式!
感謝你的閱讀,如果你有任何問題或建議,歡迎在下方留言討論。我們下次見!